/* ** Floating-point library package. ** R.E. Grehan - BYTE Magazine ** ** What you've got here is a floating-point four-banger for the ** 8088/80286/80386 family. The routines perform: ** fpadd -- addition ** fpmult - multiplication ** fpsub -- subtraction ** fpdiv -- division ** fpinput - input (converts string to floating-point) ** fpoutput - output (converts floating-point to string) ** ** This is not an "industrial strength" package, at least ** not in terms of optimization (although I have unwound ** a lot of loops). ** The C interface routines are written (of course) for BYTE ** Small C -- you should be able to use them as interfacing ** guides for the actual assembly-language floating-point ** routines. ** In the input and output routines, I've added comments in ** spots where the code could be modified to allow for freer ** formats (like, no preceding '+' sign, for example). ** See my articles in the September and October 1988 BYTEs ** for more details. ** Enjoy. ** --Rick Grehan ** ** REFERENCES: ** Microprocessor Programming for Computer Hobbyists by Neill Graham ** Tab books, 1977 ** ** Scelbi 8080 Software Gourmet Guide and Cook Book by Robert Findley ** Scelbi Computer Consulting, 1976 */ /* ** Add two floating-point numbers. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point ** numbers. ** This function performs: flt1+flt2 --> flt3. ** The function returns 0 if ok, else error code. */ fpadd(flt1,flt2,flt3) char flt1[10], flt2[10], flt3[10]; { #asm MOV BP,SP ;First move flt1 and flt2 into FAC1 and FAC2 MOV SI,6[BP] MOV DI,OFFSET FAC1_SIGN CALL LDFACC MOV SI,4[BP] MOV DI,OFFSET FAC2_SIGN CALL LDFACC ;Perform the addition CALL FPADD ;Make sure everything was ok. OR BX,BX JZ _FPADD1 XOR CX,CX ;For small C RET ;Return the result _FPADD1: MOV DI,2[BP] CALL STFAC1 XOR CX,CX ;For small C #endasm } /* ** Multiply two floating-point numbers. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point ** numbers. ** This function performs: flt1*flt2 --> flt3. ** The function returns 0 if ok, else error code. */ fpmult(flt1,flt2,flt3) char flt1[10], flt2[10], flt3[10]; { #asm MOV BP,SP ;First move flt1 and flt2 into FAC1 and FAC2 MOV SI,6[BP] MOV DI,OFFSET FAC1_SIGN CALL LDFACC MOV SI,4[BP] MOV DI,OFFSET FAC2_SIGN CALL LDFACC ;Perform the multiplication CALL FPMULT ;Make sure everything was ok. OR BX,BX JZ _FPMUL1 XOR CX,CX ;For small C RET ;Return the result _FPMUL1: MOV DI,2[BP] CALL STFAC1 XOR CX,CX ;For small C #endasm } /* ** Subtract two floating-point numbers. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point ** numbers. ** This function performs: flt1-flt2 --> flt3. ** The function returns 0 if ok, else error code. */ fpsub(flt1,flt2,flt3) char flt1[10], flt2[10], flt3[10]; { #asm MOV BP,SP ;First move flt1 and flt2 into FAC1 and FAC2 MOV SI,6[BP] MOV DI,OFFSET FAC1_SIGN CALL LDFACC MOV SI,4[BP] MOV DI,OFFSET FAC2_SIGN CALL LDFACC ;Perform the subtraction CALL FPSUB ;Make sure everything was ok. OR BX,BX JZ _FPSUB1 XOR CX,CX ;For small c RET ;Return the result _FPSUB1: MOV DI,2[BP] CALL STFAC1 XOR CX,CX ;For small c #endasm } /* ** Divide floating-point numbers. ** flt1, flt2, and flt3 are 10-byte arrays holding floating-point ** numbers. ** This function performs: flt1/flt2 --> flt3. ** The function returns 0 if ok, else error code. */ fpdiv(flt1,flt2,flt3) char flt1[10], flt2[10], flt3[10]; { #asm MOV BP,SP ;First move flt1 and flt2 into FAC1 and FAC2 MOV SI,6[BP] MOV DI,OFFSET FAC1_SIGN CALL LDFACC MOV SI,4[BP] MOV DI,OFFSET FAC2_SIGN CALL LDFACC ;Perform the division CALL FPDIV ;Make sure everything was ok. OR BX,BX JZ _FPDIV1 XOR CX,CX ;For small c RET ;Return the result _FPDIV1: MOV DI,2[BP] CALL STFAC1 XOR CX,CX ;For small c #endasm } /* ** Input a floating-point number. ** Accepts pointer to a string that must be in the ** form: sd.ddddEsdd ** Also accepts pointer to an array of 10 bytes, into which ** the floating-point number is stored. */ fpinput(str,fnum) char *str; char fnum[10]; { #asm MOV BP,SP ;Put pointer to string in SI and call conversion routine MOV SI,4[BP] CALL FPINP ;Move the result into fnum MOV DI,2[BP] CALL STFAC1 XOR CX,CX ;For small c #endasm } /* ** Output a floating-point number. ** Accepts pointer to a 10-byte array containing an floating- ** point number. Also accepts pointer to a buffer, into ** which the number is stored in the format: ** sd.ddddEsdd ** Number of digits to the right of the decimal place is ** specified in n. */ fpoutput(fnum,buff,n) char fnum[10]; char *buff; int n; { #asm MOV BP,SP ;First move the number into FAC1 MOV SI,6[BP] MOV DI,OFFSET FAC1_SIGN CALL LDFACC ;Do output MOV CX,2[BP] MOV DI,4[BP] CALL FPOUT XOR CX,CX ;For small c #endasm } /* ** Actual floating-point routines follow. */ #asm ; ; Definitions for the floating-point accumulators ; FAC1_SIGN DB ? ;FP ACCUM1 - SIGN (80h=negative, 0=positive) FAC1_EXP DW ? ; - EXPONENT FAC1_MAN DW 5 DUP (?) ; - MANTISSA FAC2_SIGN DB ? ;Accumulator 2 FAC2_EXP DW ? FAC2_MAN DW 5 DUP (?) ;Third accumulator for mantissa only - used as a temp location for ;calculations. FAC3_MAN DW 5 DUP (?) EXP_SIGN DB ? ;Sign of decimal exponent ;Equates BIAS EQU 16384 ;Bias for exponent MAXEXP EQU 32768 ;Maximum exponent ;Error codes returned DIVZER EQU 1 ;Divide by zero error EXPOVF EQU 2 ;Exponent overflow error EXPUND EQU 3 ;Exponent underflow error ;********** ;Load a floating-point accumulator. ;Assumes SI points to a floating-point number and DI points ;to the _SIGN field of the accumulator to load. PUBLIC LDFACC LDFACC: CLD ;Set direction to increment ;Get the sign bit MOV AX,[SI] AND AH,80H MOV [DI],AH INC DI ;Move the rest MOV BX,DI ;Save destination location MOV CX,5 REP MOVSW MOV WORD PTR [DI],0 ;Clear sign bit from exponent AND WORD PTR [BX],7FFFH INC BX INC BX ;Shift the mantissa right 1 bit SHR WORD PTR [BX],1 RCR WORD PTR 2[BX],1 RCR WORD PTR 4[BX],1 RCR WORD PTR 6[BX],1 RCR WORD PTR 8[BX],1 LDFACC1: RET ;********** ;Store FACC1 to the memory location ;pointed to by DI. ;NOTE: THIS ROUTINE MUNGES FAC1 IN THE PROCESS ; OF STORING IT TO MEMORY. NOT A GOOD IDEA, ; PARTICULARLY IF YOU'RE DOING LOT'S OF BACK- ; TO-BACK COMPUTATIONS AND YOU WANT TO ; OPTIMIZE THINGS. (I just thought I should ; bring this to your attention.--rg) PUBLIC STFAC1 STFAC1: ;Set direction CLD ;Shift the mantissa left 1 bit SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 ;Set the sign bit based on FAC1_SIGN CMP FAC1_SIGN,0 JNE STFC1 AND FAC1_EXP,7FFFH JMP SHORT STFC2 STFC1: OR FAC1_EXP,8000H STFC2: ;Move it all over MOV SI,OFFSET FAC1_EXP MOV CX,5 REP MOVSW RET ;********** ;Floating-point add routine ;Arguments assumed to be in FAC1 ; and FAC2. Result in FAC1 PUBLIC FPADD FPADD: ;Determine which argument has the ;larger exponent. Move the number with the ;larger exponent into FAC1 and the number with the ;smaller exponent into FAC2. MOV AX,FAC1_EXP CMP AX,FAC2_EXP JAE FADD1 MOV CX,11 MOV SI,OFFSET FAC1_SIGN MOV DI,OFFSET FAC2_SIGN FADD0: MOV AL,[SI] XCHG AL,[DI] MOV [SI],AL INC SI INC DI LOOP FADD0 ;Now figure out how many bits to ;shift the mantissa of FAC2 in order ;to align the radix point. If this ;amount is greater than 78, we can ;go home, cause the result is already ;in FAC1. FADD1: MOV AX,FAC1_EXP SUB AX,FAC2_EXP CMP AX,78 JL FADD2 XOR BX,BX ;Show all ok. RET ;See if the signs of FAC1 and ;FAC2 differ. If so, negate the ;mantissa of FAC2. FADD2: MOV BL,FAC1_SIGN CMP BL,FAC2_SIGN JZ FADD3 MOV BX,OFFSET FAC2_MAN CALL NEGBX ;Now shift FAC2 right by amount ;given in AX (which is the difference in the ;exponent amounts). FADD3: MOV CX,AX OR CX,CX ;Shift necessary? JZ FADD4A FADD4: SHR FAC2_MAN,1 RCR FAC2_MAN+2,1 RCR FAC2_MAN+4,1 RCR FAC2_MAN+6,1 RCR FAC2_MAN+8,1 LOOP FADD4 ;Add the two mantissas. FADD4A: MOV AX,FAC2_MAN+8 ADD FAC1_MAN+8,AX MOV AX,FAC2_MAN+6 ADC FAC1_MAN+6,AX MOV AX,FAC2_MAN+4 ADC FAC1_MAN+4,AX MOV AX,FAC2_MAN+2 ADC FAC1_MAN+2,AX MOV AX,FAC2_MAN ADC FAC1_MAN,AX ;See if the sign of the two operands ;differ. If so, and if the result ;of the addition was negative, negate ;the FAC1 mantissa and move the sign ;of FAC2 to FAC1. MOV AL,FAC1_SIGN CMP AL,FAC2_SIGN JZ FADD5 MOV AX,FAC1_MAN OR AX,AX JNS FADD5 MOV BX,OFFSET FAC1_MAN CALL NEGBX MOV AL,FAC2_SIGN MOV FAC1_SIGN,AL ;Now normalize the contents of FAC1 FADD5: JMP NORM_FAC1 ;********** ;Floating-point subtract. ;Subtract contents of FAC2 from ;FAC1 and leave the result in FAC1. ; PUBLIC FPSUB FPSUB: ;Flip the sign of FAC2 XOR FAC2_SIGN,80H ;And do an ADD JMP FPADD ;********** ;Floating-point multiply ;Multiply FAC1 and FAC2 - result in FAC1 PUBLIC FPMULT FPMULT: ;First set up the signs of the result. Do this by ;performing an exclusive-OR on the signs of the operands MOV AL,FAC2_SIGN XOR FAC1_SIGN,AL ;Now calculate the exponent of the result MOV AX,FAC2_EXP ADD AX,FAC1_EXP SUB AX,BIAS+1 MOV FAC1_EXP,AX ;Clear FAC3 to hold results XOR AX,AX MOV FAC3_MAN,AX MOV FAC3_MAN+2,AX MOV FAC3_MAN+4,AX MOV FAC3_MAN+6,AX MOV FAC3_MAN+8,AX ;Set up counter MOV CX,79 ;Shift FAC1 and result 1 bit to the right. ;If the bit shifted out of FAC1 is a 1, add FAC2 ;to the result. FMUL1: SHR FAC3_MAN,1 RCR FAC3_MAN+2,1 RCR FAC3_MAN+4,1 RCR FAC3_MAN+6,1 RCR FAC3_MAN+8,1 ; SHR FAC1_MAN,1 RCR FAC1_MAN+2,1 RCR FAC1_MAN+4,1 RCR FAC1_MAN+6,1 RCR FAC1_MAN+8,1 JNC FMUL2 MOV AX,FAC2_MAN+8 ADD FAC3_MAN+8,AX MOV AX,FAC2_MAN+6 ADC FAC3_MAN+6,AX MOV AX,FAC2_MAN+4 ADC FAC3_MAN+4,AX MOV AX,FAC2_MAN+2 ADC FAC3_MAN+2,AX MOV AX,FAC2_MAN ADC FAC3_MAN,AX FMUL2: LOOP FMUL1 ;Move the result into FAC1 FMUL3: MOV SI,OFFSET FAC3_MAN MOV DI,OFFSET FAC1_MAN MOV CX,5 REP MOVSW ;Exit via the normalization routine JMP NORM_FAC1 ;********** ;Floating-point divide ;Divide FAC1 by FAC2 - result in FAC1 PUBLIC FPDIV FPDIV: ;Verify that we are not trying to divide by zero. ;Do this by checking the exponent of FAC2. MOV AX,FAC2_EXP OR AX,AX JNE FDIV0 MOV BX,DIVZER ;Indicate error RET ;Go home ;Figure out the sign of the result FDIV0: MOV AL,FAC2_SIGN XOR FAC1_SIGN,AL ;And calculate the resulting exponent MOV AX,FAC1_EXP SUB AX,FAC2_EXP ADD AX,BIAS ;Fix bias MOV FAC1_EXP,AX ;Now perform the division, using nonrestoring fraction ;division. First, clear the result. XOR AX,AX MOV FAC3_MAN,AX MOV FAC3_MAN+2,AX MOV FAC3_MAN+4,AX MOV FAC3_MAN+6,AX MOV FAC3_MAN+8,AX ;Do an initial subtraction MOV AX,FAC2_MAN+8 SUB FAC1_MAN+8,AX MOV AX,FAC2_MAN+6 SBB FAC1_MAN+6,AX MOV AX,FAC2_MAN+4 SBB FAC1_MAN+4,AX MOV AX,FAC2_MAN+2 SBB FAC1_MAN+2,AX MOV AX,FAC2_MAN SBB FAC1_MAN,AX ;Set up loop count MOV CX,80 FDIV1: ;Shift quotient left SHL FAC3_MAN+8,1 RCL FAC3_MAN+6,1 RCL FAC3_MAN+4,1 RCL FAC3_MAN+2,1 RCL FAC3_MAN,1 ;Shift dividend left SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 ;If 1 bit shifted out of dividend (indicating that ; the result of the subtraction was negative, ; restore by adding divisor back in. JNC FDIV2 MOV AX,FAC2_MAN+8 ADD FAC1_MAN+8,AX MOV AX,FAC2_MAN+6 ADC FAC1_MAN+6,AX MOV AX,FAC2_MAN+4 ADC FAC1_MAN+4,AX MOV AX,FAC2_MAN+2 ADC FAC1_MAN+2,AX MOV AX,FAC2_MAN ADC FAC1_MAN,AX JMP SHORT FDIV3 ;If 0 bit shifted out of dividend, continue process, but make ;sure you set a 1 bit in the quotient. FDIV2: MOV AX,FAC2_MAN+8 SUB FAC1_MAN+8,AX MOV AX,FAC2_MAN+6 SBB FAC1_MAN+6,AX MOV AX,FAC2_MAN+4 SBB FAC1_MAN+4,AX MOV AX,FAC2_MAN+2 SBB FAC1_MAN+2,AX MOV AX,FAC2_MAN SBB FAC1_MAN,AX OR FAC3_MAN+8,1 ;Set lo bit of quotient to 1 FDIV3: LOOP FDIV1 ;Move quotient back into FAC1 and normalize ; (borrow the code already in FMUL to do this.) JMP FMUL3 ; ;********** ;NORMALIZE FAC1 ;Call this at the end of floating-point operations, and ;before you store the floating-point number. ; PUBLIC NORM_FAC1 NORM_FAC1: ;If FAC1's mantissa is zero, claer FAC1's exponent and set ;it's sign to 0 to indicate true zero MOV AX,FAC1_MAN OR AX,FAC1_MAN+2 OR AX,FAC1_MAN+4 OR AX,FAC1_MAN+6 OR AX,FAC1_MAN+8 JNE NORM0 MOV FAC1_SIGN,AL MOV FAC1_EXP,AX XOR BX,BX ;Show all ok RET ;If we have a 1 in highmost bit - shift FAC1 to the right and ;increment exponent NORM0: MOV BX,FAC1_EXP TEST FAC1_MAN,8000H JZ NORM1 SHR FAC1_MAN,1 RCR FAC1_MAN+2,1 RCR FAC1_MAN+4,1 RCR FAC1_MAN+6,1 RCR FAC1_MAN+8,1 INC BX ; ;Shift FAC1 left until we get a 1 in next-to-highmost bit. ;Subtract 1 from FAC1's exponent each time we do a shift. NORM1: TEST FAC1_MAN,4000H JNZ NORM2 SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 DEC BX JMP NORM1 ; NORM2: MOV FAC1_EXP,BX ;Check to see if the exponent overflowed CMP BX,MAXEXP JB NORM3 MOV BX,EXPOVF ;Error code RET ;Check to see if the exponent underflowed NORM3: OR BX,BX JNE NORM4 MOV BX,EXPUND RET NORM4: XOR BX,BX ;Show all ok RET ; ;Routine to negate the mantissa pointed ;to by BX. NEGBX: MOV CX,5 STC NEG1: NOT WORD PTR 8[BX] ADC WORD PTR 8[BX],0 DEC BX DEC BX LOOP NEG1 RET ;********** ;FLOATING-POINT INPUT ROUTINE ;Assume SI points to a string containing: ; sX.XXXXXEsXXn ; Where s= + or - ; X is 0-9 ; n is null PUBLIC FPINP FPINP: ;First set the direction for increment CLD ;Get the sign LODSB CMP AL,'+' JNE INP1 MOV AL,0 JMP SHORT INP2 INP1: MOV AL,80h INP2: MOV FAC1_SIGN,AL ;Clear CX to hold accumulated value of decimal exponent XOR CX,CX ;Clear FAC1 MOV FAC1_MAN,CX MOV FAC1_MAN+2,CX MOV FAC1_MAN+4,CX MOV FAC1_MAN+6,CX MOV FAC1_MAN+8,CX ;Get the digit to the left of the decimal point and increment ;decimal exponent LODSB CALL ADDIGIT ;Skip past the decimal point. ;**NOTE: Add code to verify format here. LODSB ;Read digits to the right of the decimal point INP3: LODSB CMP AL,'0' JL INP4 ;Jump if not digit (must be 'E') CMP AL,'9' JG INP4 ;Jump if not digit (must be 'E') CALL ADDIGIT DEC CX ;Decr. decimal accumulator JMP INP3 ;Skip past the 'E'. ;**NOTE: Add code to verify format here. INP4: ;Read in exponent sign. LODSB CMP AL,'+' JNE INP5 MOV AL,0 ;Must be negative sign JMP SHORT INP6 INP5: MOV AL,80H INP6: MOV EXP_SIGN,AL ;Read in exponent digits XOR BX,BX ;Use as accumulator XOR AX,AX MOV DL,10 ;Multiplier INP7: LODSB CMP AL,'0' JL INP8 ;Jump if not digit (must be null) CMP AL,'9' JG INP8 ;Jump if not digit (must be goofup) SUB AL,'0' ;Make it a true number XCHG BX,AX ;Accumulated value in AX MUL DL ADD AX,BX ;Add in digit XCHG BX,AX ;Accumulated value back in BX JMP INP7 ;Fix up the accumulated exponent in BX INP8: MOV AL,EXP_SIGN OR AL,AL JNE INP9 ;Exponent sign is positive ADD CX,BX JMP SHORT INP9A ;Exponent sign is negative INP9: SUB CX,BX ;Normalize FAC1 INP9A: MOV FAC1_EXP,BIAS+79 CALL NORM_FAC1 ;Load FAC2 with a floating point 10.0 XOR AX,AX MOV FAC2_SIGN,AL MOV FAC2_EXP,BIAS+4 MOV FAC2_MAN,5000H MOV FAC2_MAN+2,AX MOV FAC2_MAN+4,AX MOV FAC2_MAN+6,AX MOV FAC2_MAN+8,AX ;Examine the contents of CX (our decimal exponent). If CX=-n, divide ;FAC1 by 10 n times. If CX=+n, multiply FAC1 by n. MOV DX,CX OR DX,DX JE INP11 ;Jump if nothing to do. JL INP12 ;Multiply INP10: CALL FPMULT DEC DX JNE INP10 INP11: RET ;Divide INP12: NEG DX INP13: CALL FPDIV DEC DX JNE INP13 RET ; ADDIGIT: CALL FAC1_MBY10 ;Multiply mantissa by 10 ;add in the digit AND AX,0FH ADD FAC1_MAN+8,AX ADC FAC1_MAN+6,0 ADC FAC1_MAN+4,0 ADC FAC1_MAN+2,0 ADC FAC1_MAN,0 RET ;********** ;Floating-point output routine ;Stores the number in FAC1 into memory location ;pointed to by DI. CX contains the number of digits ;to the right of the decimal place to output. ;Number is stored as a string: ; sd.dddddddEsddd ; s is + or - and d is a digit. ;Note: the mantissa can hold 19 significant digits. PUBLIC FPOUT FPOUT: CLD PUSH CX ;Save ;First see if we have true zero. If so we can skip ;a lot. CMP FAC1_EXP,0 JZ FOUT3 ;Put a floating-point 10 in FAC2. We'll use it later XOR AX,AX MOV FAC2_SIGN,AL MOV FAC2_MAN+2,AX MOV FAC2_MAN+4,AX MOV FAC2_MAN+6,AX MOV FAC2_MAN+8,AX MOV FAC2_MAN,5000H MOV FAC2_EXP,BIAS+4 ;Use DX to hold the decimal exponent XOR DX,DX ;Divide by 10 while FAC1 is >15 (Check for this by watching ;the exponent). For each division, increment DX by 1. FOUT1: CMP FAC1_EXP,BIAS+4 JBE FOUT2 CALL FPDIV INC DX JMP FOUT1 ;Multiply by 10 while FAC1 is <1. For each multiplication, ;decrement DX by 1. FOUT2: CMP FAC1_EXP,BIAS+1 JA FOUT2A CALL FPMULT DEC DX JMP FOUT2 ;If number is greater than or equal to 10, do one more ;division by 10 to get things in line. And don't forget to ;increment DX. FOUT2A: CMP FAC1_EXP,BIAS+4 JNE FOUT3 CMP FAC1_MAN,5000H JL FOUT3 CALL FPDIV INC DX ;Position binary point between bits 75 and 76 so we can ;isolate the integer portion of the floating point number. FOUT3: CMP FAC1_EXP,BIAS+4 JNE FOUT4 SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 JMP SHORT FOUT5 FOUT4: CMP FAC1_EXP,BIAS+3 JGE FOUT4A SHR FAC1_MAN,1 RCR FAC1_MAN+2,1 RCR FAC1_MAN+4,1 RCR FAC1_MAN+6,1 RCR FAC1_MAN+8,1 INC FAC1_EXP JMP FOUT4 ;Do any rounding here. (I'm not happy with what I've got here... ;perhaps a better rounding method could be found.) FOUT4A: ADD FAC1_MAN+8,9 ADC FAC1_MAN+6,0 ADC FAC1_MAN+4,0 ADC FAC1_MAN+2,0 ADC FAC1_MAN,0 ;Make sure we didn't overflow CMP FAC1_MAN,0A000H JB FOUT5 XOR AX,AX MOV FAC1_MAN+8,AX MOV FAC1_MAN+6,AX MOV FAC1_MAN+4,AX MOV FAC1_MAN+2,AX MOV FAC1_MAN,1000H INC DX ;Save the sign FOUT5: CMP FAC1_SIGN,0 JNE FOUT5A MOV BYTE PTR [DI],'+' JMP SHORT FOUT5B FOUT5A: MOV BYTE PTR [DI],'-' FOUT5B: INC DI ;Get leftmost digit MOV AL,BYTE PTR FAC1_MAN+1 SHR AL,1 SHR AL,1 SHR AL,1 SHR AL,1 ;Digit now in low nibble ADD AL,'0' STOSB ;OUTPUT the decimal point MOV AL,'.' STOSB ;Output the remaining digits. POP CX ;Restore saved count FOUT6: AND FAC1_MAN,0FFFH ;Strip integer portion CALL FAC1_MBY10 ;Multiply by 10 MOV AL,BYTE PTR FAC1_MAN+1 SHR AL,1 ;Get next integer part SHR AL,1 SHR AL,1 SHR AL,1 ADD AL,'0' STOSB LOOP FOUT6 ;Get the exponent portion MOV AL,'E' STOSB OR DX,DX JS FOUT7 MOV AL,'+' JMP SHORT FOUT8 FOUT7: MOV AL,'-' NEG DX ;Absolute value of exp. in DX FOUT8: STOSB ;Following routine gets the exponent value. Note that we use ;division by 10 to strip off digits as remainders. This means ;the number comes out in right-to-left order...backwards. So ;we have to 'flip' the number when we're done. MOV AX,DX OR AX,AX ;0 exponent? JZ FOUT10 MOV SI,DI ;Save location in buffer MOV CX,10 FOUT9: XOR DX,DX ;Clear for division DIV CX FOUT10: ADD DL,'0' ;Digit in DX MOV [DI],DL INC DI OR AX,AX ;Done? JNE FOUT9 MOV BYTE PTR [DI],0 ;Store null ;Fix the exponent so it reads left-to-right. DEC DI FOUT11: CMP SI,DI ;Finished flipping? JAE FOUT12 MOV AL,[DI] ;Swap XCHG AL,[SI] MOV [DI],AL INC SI DEC DI JMP FOUT11 ;Exit FOUT12: RET ;********** ;Multiply contents of FAC1_MAN by 10. ;Do this using the formula: 10*x = 2*x + 8*x and ;the fact that you can multiply by 2 and multiply by ;8 by doing left shifts. ;Note that this only multiplies manissas...not the entire FP number. FAC1_MBY10: PUSH AX SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 ; MOV AX,FAC1_MAN MOV FAC3_MAN,AX MOV AX,FAC1_MAN+2 MOV FAC3_MAN+2,AX MOV AX,FAC1_MAN+4 MOV FAC3_MAN+4,AX MOV AX,FAC1_MAN+6 MOV FAC3_MAN+6,AX MOV AX,FAC1_MAN+8 MOV FAC3_MAN+8,AX ; SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 ; SHL FAC1_MAN+8,1 RCL FAC1_MAN+6,1 RCL FAC1_MAN+4,1 RCL FAC1_MAN+2,1 RCL FAC1_MAN,1 ; MOV AX,FAC3_MAN+8 ADD FAC1_MAN+8,AX MOV AX,FAC3_MAN+6 ADC FAC1_MAN+6,AX MOV AX,FAC3_MAN+4 ADC FAC1_MAN+4,AX MOV AX,FAC3_MAN+2 ADC FAC1_MAN+2,AX MOV AX,FAC3_MAN ADC FAC1_MAN,AX POP AX ; RET #endasm